<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>canvas</title>
		<style>
			* {
				margin: 0;
				padding: 0;
			}

			canvas {
				display: block;
				margin: 30px auto;
				cursor: pointer;
				overflow: hidden;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas" width="240" height="76" onclick="paint()">

		</canvas>
		<script>
			paint();

			function paint() {
				const canvas = document.getElementById("canvas");
				const ctx = canvas.getContext("2d");
				// 绘制验证码背景
				drawBg(ctx);
				// 生成随机字符串
				let code = randomCode(6);
				// 绘制验证码
				drawText(ctx, code);
				// 绘制干扰圆点
				paintDot(ctx, 40);
				// 绘制干扰线
				paintLine(ctx, 6)
			}

			function randomNum(min, max) {
				return Math.floor(Math.random() * (max - min) + min)
			}

			function randomCode(len) {
				let code = "";
				let arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
					'u', 'v', 'w', 'x', 'y', 'z', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
				]
				for (let i = 0; i < len; i++) {
					let randomIndex = randomNum(0, arr.length);
					code += arr[randomIndex];
				}
				return code;
			}

			/**
			 * 整体思路是移动canvas上下文, 然后通过旋转上下文来实现文字的渲染,
			 * 每次循环开始先保存一下画笔, 然后向右移动一个格, 先旋转上下文, 在绘制文字
			 * @param {Object} ctx
			 * @param {Object} text
			 */
			function drawText(ctx, text) {
				for (let i = 0; i < text.length; i++) {
					// 先保存一下画笔, 绘制完这个字符在重置画笔
					ctx.save();
					// 随机生成验证码字号
					ctx.font = randomNum(24, 40) + "px serif";
					// 随机生成一个画笔颜色
					let color = randomRGBColor(85, 240);
					ctx.fillStyle = color;
					// 随机坐标, 通过控制画布移动来控制每个字符在不同位置
					const posX = randomNum(40 * i + 20, 40 * (i + 1) - 20);
					const posY = randomNum(30, 70)
					ctx.translate(posX, posY);
					// 随机生成字符旋转角度
					let randomRotate = randomNum(-30, 30)
					ctx.rotate((Math.PI / 180) * randomRotate)
					// 因为画布已经移动, 因此原地画字符即可
					ctx.fillText(text[i], 0, 0)
					// 还原画笔, 将画笔设置还原, 比如平移, 旋转, 颜色设置等等
					ctx.restore();
				}
			}

			/**
			 * 绘制干扰圆点
			 */
			function paintDot(ctx, num = 20) {
				for (let i = 0; i < num; i++) {
					ctx.fillStyle = randomRGBColor(85, 255);
					ctx.beginPath();
					// 随机位置x, y
					let dotX = randomNum(2, 238);
					let dotY = randomNum(2, 74);
					ctx.arc(dotX, dotY, 1, 0, 2 * Math.PI)
					ctx.stroke();
					ctx.fill();
					ctx.closePath();
				}
			}

			/**
			 * 绘制干扰线
			 */
			function paintLine(ctx, num = 5) {
				for (let i = 0; i < num; i++) {
					ctx.strokeStyle = randomRGBColor(85, 235);
					ctx.lineWidth = 2
					ctx.beginPath();
					ctx.moveTo(randomNum(0, 240), randomNum(0, 76));
					ctx.lineTo(randomNum(0, 240), randomNum(0, 76));
					ctx.stroke();
					ctx.closePath();
				}
			}

			/**
			 * 生成随机颜色rgb字符串
			 * @param {Object} minColor
			 * @param {Object} maxColor
			 */
			function randomRGBColor(minColor, maxColor) {
				return `rgb(${randomNum(minColor, maxColor)},${randomNum(minColor, maxColor)},${randomNum(minColor, maxColor)})`
			}

			/**
			 * 绘制背景色
			 * @param {Object} ctx
			 */
			function drawBg(ctx) {
				let color = randomRGBColor(140, 240);
				ctx.fillStyle = color
				ctx.fillRect(0, 0, 240, 76);
			}
		</script>
	</body>
</html>

